package com.ioc.context;


import com.ioc.annotation.Component;
import com.ioc.annotation.ComponentScan;
import com.ioc.annotation.Configuration;
import com.ioc.annotation.Inject;
import com.ioc.exception.MicroContextInitException;
import com.ioc.util.ClassLoaderUtils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * ioc容器上下文的实现
 * @author lishouyu
 */
public class MicroContext {

    /**
     * ioc的上下文容器
     */
    private static volatile Map<Class<?>,Object> applicationContext = null;

    /**
     * 私有化构造方法
     */
    private MicroContext(){

    }

    /**
     * 根据类类型获取bean,必修先调用初始化上下文方法,然后在调用getBean方法
     * @param classType 要获取bean的类类型
     * @return
     */
    public static Object getBean(Class<?> classType){
        return applicationContext.get(classType);
    }

    /**
     * 初始化上下文
     * @param configClass 配置类的类类型
     */
    public static void init(Class<?> configClass,String[] args){
        getApplicationInstance(configClass);
    }

    /**
     * 保证上下文容器只会被初始化一次
     * @param configClass 配置类的类类型
     * @return
     */
    private static Map<Class<?>,Object> getApplicationInstance(Class<?> configClass){
        if (applicationContext == null){
            synchronized (MicroContext.class){
                if (applicationContext == null){
                    // 1. 对ioc容器进行初始化
                    // 1.1 查找ioc所需要收集,注入bean的范围.对bean进行收集
                    if (configClass.isAnnotationPresent(Configuration.class) && configClass.isAnnotationPresent(ComponentScan.class)) {
                        ComponentScan annotation = configClass.getAnnotation(ComponentScan.class);
                        String[] packages = annotation.basePackages();
                        ConcurrentHashMap<Class<?>,Object> applicationContext = new ConcurrentHashMap<>(1<<5);
                        if (packages.length<1){
                            // 扫描配置类所在的包,加载所有的bean
                            String name = configClass.getName();
                            String basePackage = name.substring(0, name.lastIndexOf("."));
                            Enumeration<URL> urls = null;
                            try {
                                urls = ClassLoaderUtils.getClassLoader().getResources(basePackage.replaceAll("\\.", "/"));
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                            assert urls != null;
                            while (urls.hasMoreElements()) {
                                // 递归加载包下所有的bean
                                collectionBean(urls.nextElement().getPath(),applicationContext,basePackage);
                            }
                        }else{
                            // 扫描指定的包,加载所有的bean
                            for (String aPackage : packages) {
                                Enumeration<URL> urls = null;
                                try {
                                    String packageUrl = aPackage.replaceAll("\\.", "/");
                                    urls = ClassLoaderUtils.getClassLoader().getResources(packageUrl);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                                assert urls != null;
                                while (urls.hasMoreElements()) {
                                    // 递归加载包下所有的bean
                                    collectionBean(urls.nextElement().getPath(), applicationContext,aPackage);
                                }
                            }
                        }
                        // 1.2对bean中的依赖进行注入
                        initIoc(applicationContext);
                        // 1.3 依赖注入完毕
                        MicroContext.applicationContext = applicationContext;
                    }else{
                        throw new MicroContextInitException("主配置类必须带有com.micro.fast.ioc.annotation.Configuration和" +
                                "com.micro.fast.ioc.annotation.ComponentScan注解");
                    }
                    return applicationContext;
                }else {
                    return applicationContext;
                }
            }
        }else {
            return applicationContext;
        }
    }

    /**
     * 根据包名收集bean
     * @param packagePath 包名
     */
    private static void collectionBean(String packagePath,ConcurrentHashMap<Class<?>,Object> applicationContext,String packageName){
        File[] classFileAndDir = getClassFile(packagePath);
        if (classFileAndDir!=null){
            for (File file : classFileAndDir) {
                if (file.isDirectory()){
                    // 对路径下的目录进行递归
                    collectionBean(file.getPath(),applicationContext,packageName + "." + file.getName());
                }else{
                    // 判断类上有没有Component注解
                    try {
                        Class<?> aClass = Class.forName(packageName + "." + file.getName().substring(0,file.getName().lastIndexOf(".")));
                        if (isAComponent(aClass)) {
                            // 如果有component注解就收集到bean中
                            applicationContext.put(aClass,getComponentInfo(aClass));
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }


    /**
     * 根据一个类的类类型判断是不是一个组件
     * @param cls 类的类类型
     * @return 是否是一个组件
     */
    private static boolean isAComponent(Class<?> cls){
        if (cls.isAnnotationPresent(Component.class)) {
            return true;
        }
        return false;
    }

    /**
     * 获取一个Component的信息并填入容器中
     * @param cls 组件类的类类型
     */
    public static Object getComponentInfo(Class<?> cls) throws IllegalAccessException, InstantiationException {
       return cls.newInstance();
    }

    /**
     * 进行依赖注入操作
     */
    private static void initIoc(Map<Class<?>,Object> applicationContext){
        if(applicationContext!=null){
            applicationContext.forEach((key,value) ->{
                Field[] fields = key.getDeclaredFields();
                if (fields!= null){
                    for (Field field : fields) {
                        if (field.isAnnotationPresent(Inject.class)) {
                            Class<?> type = field.getType();
                            field.setAccessible(true);
                            try {
                                // 进行依赖注入
                                field.set(value,applicationContext.get(type));
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            });
        }
    }

    /**
     * 加载该路径下所有的class文件和目录
     */
    private static File[] getClassFile(String filePath) {
        return new File(filePath).listFiles(file -> (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory());
    }

}
